#ifndef INCLUDE_ORM_MAPPER_H_
#define INCLUDE_ORM_MAPPER_H_

#include <cctype>
#include <iostream>
#include <list>
#include <string>
#include <type_traits>
#include <vector>

#include "/usr/local/Cellar/mysql-client/8.0.25/include/mysql/mysql.h"
#include "sql_connector.h"

/**
 * @brief 宏定义，__VA_ARFS__可变参数列表
 *
 */
#define ORMAP(_MY_CLASS_, ...)                            \
 private:                                                 \
  friend class ORMapper;                                  \
  template <typename VISITOR, typename FN>                \
  void __Accept(const VISITOR &visitor, FN fn) {          \
    visitor.Visit(fn, __VA_ARGS__);                       \
  }                                                       \
  template <typename VISITOR, typename FN>                \
  void __Accept(const VISITOR &visitor, FN fn) const {    \
    visitor.Visit(fn, __VA_ARGS__);                       \
  }                                                       \
  constexpr static const char *__ClassName = #_MY_CLASS_; \
  constexpr static const char *__FieldNames = #__VA_ARGS__;

/**
 * @brief 查询条件类
 *
 */
struct Expr {
  std::vector<std::pair<const void *, std::string>> expr;
  template <typename T>
  Expr(const T &property, const std::string &relOp, const T &value)
      : expr{std::make_pair(&property, relOp)} {
    std::ostringstream os;
    SerializeValue(os, value);
    expr.front().second.append(os.str());
  }
  template <typename T>
  Expr(const T &property, const std::string &relOp = "=")
      : Expr(property, relOp, property) {}

  inline Expr operator&&(const Expr &right) { return And_Or(right, " and "); }
  inline Expr operator||(const Expr *right) { return And_Or(right, " or "); }

 private:
  Expr And_Or(const Expr &right, std::string logOp) {
    const void *ptr = nullptr;
    expr.emplace(expr.begin(), std::make_pair(ptr, std::string("(")));
    expr.emplace_back(std::make_pair(ptr, std::move(logOp)));
    for (const auto exprPair : right.expr) expr.emplace_back(exprPair);
    expr.emplace_back(std::make_pair(ptr, std::string(")")));
    return *this;
  }
};

template <typename T>
struct Expr_Field {
  const T &_property;
  Expr_Field(const T &property) : _property(property){};
  inline Expr operator==(T value) {
    return Expr{_property, "=", std::move(value)};
  }
  inline Expr operator!=(T value) {
    return Expr{_property, "!=", std::move(value)};
  }
  inline Expr operator>(T value) {
    return Expr{_property, ">", std::move(value)};
  }
  inline Expr operator>=(T value) {
    return Expr{_property, ">=", std::move(value)};
  }
  inline Expr operator<(T value) {
    return Expr{_property, "<", std::move(value)};
  }
  inline Expr operator<=(T value) {
    return Expr{_property, "<=", std::move(value)};
  }
};

/**
 * @brief ORM操作类
 *
 */
class ORMapper {
 public:
  ORMapper() : _connector() {}
  ORMapper(const std::string &host, const std::string &user,
           const std::string &passwd, const std::string &db_name,
           int port = 3306)
      : _connector() {
    initConn(host, user, passwd, db_name, port);
  }

  bool initConn(const std::string &host, const std::string &user,
                const std::string &passwd, const std::string &db_name,
                int port = 3306) {
    return _connector.initDB(host, user, passwd, db_name, port);
  }

  /**
   * @brief 事务操作
   *
   * @tparam Fn 回掉函数类型
   * @param fn 回掉函数
   */
  template <typename Fn>
  void Transaction(Fn fn) {
    try {
      _connector.Execute("begin;");
      fn();
      _connector.Execute("commit;");
    } catch (...) {
      _connector.Execute("rollback;");
      throw;
    }
  }

  /**
   * @brief 创建mysql表
   *
   * @tparam C 模型类型
   * @param entity 模型
   */
  template <typename C>
  void CreateTbl(const C &entity) {
    // 获取参数名称。[[id],[name],[score]]
    const auto &fieldNames = ORMapper::_FieldNames<C>();
    std::vector<std::string> strTypes(fieldNames.size());
    size_t index = 0;

    // visitor.Visit(fn, id, name, score);
    // [](id) [](name) [](score)
    entity.__Accept(FnVisitor(), [&strTypes, &index](auto &val) {
      strTypes[index++] = TypeString(val);  // [[INT], [DOUBLE], [VARCHAR]]
      return true;
    });

    auto strFmt = fieldNames[0] + " " + strTypes[0] +
                  " primary key,";  // "id INT primary key,"
    for (size_t i = 1; i < fieldNames.size(); i++)
      strFmt += fieldNames[i] + " " + strTypes[i] +
                ",";  // "id INT primary key,name DOUBLE, ..."
    strFmt.pop_back();
    // "create table id INT primary key,name DOUBLE, ..."
    _connector.Execute("create table " + std::string(C::__ClassName) + "(" +
                       strFmt + ");");
  }

  /**
   * @brief 删除数据库表
   *
   * @tparam C
   */
  template <typename C>
  void DropTbl(const C &) {
    _connector.Execute("drop table " + std::string(C::__ClassName) + ";");
  }

  /**
   * @brief 数据库表中插入数据
   *
   * @tparam C
   * @param entity
   */
  template <typename C>
  void Insert(const C &entity) {
    std::ostringstream os;
    size_t index = 0;
    auto fieldCount = ORMapper::_FieldNames<C>().size();

    entity.__Accept(FnVisitor(), [&os, &index, fieldCount](auto &val) {
      if (++index != fieldCount)
        SerializeValue(os, val) << ',';
      else
        SerializeValue(os, val);
      return true;
    });

    // "insert into MyClass values ( ... )"
    _connector.Execute("insert into " + std::string(C::__ClassName) +
                       " values (" + os.str() + ");");
  }

  /**
   * @brief 循环插入数据
   *
   * @tparam In
   * @param entities
   */
  template <typename In>
  void InsertRange(const In &entities) {
    using C = typename In::value_type;

    std::ostringstream os;
    size_t count = 0;
    auto fieldCount = ORMapper::_FieldNames<C>().size();

    for (const auto &entity : entities) {
      os << "(";

      size_t index = 0;
      entity.__Accept(FnVisitor(), [&os, &index, fieldCount](auto &val) {
        if (++index != fieldCount)
          SerializeValue(os, val) << ',';
        else
          SerializeValue(os, val);
        return true;
      });

      if (++count != entities.size())
        os << "),";
      else
        os << ")";
    }

    _connector.Execute("insert into " + std::string(C::__ClassName) +
                       " values " + os.str() + ";");
  }

  /**
   * @brief 更新数据
   *
   * @tparam C
   * @param entity
   */
  template <typename C>
  void Update(const C &entity) {
    std::stringstream os, osKey;
    size_t index = 0;
    const auto &fieldNames = ORMapper::_FieldNames<C>();

    os << "set ";
    osKey << "where ";

    entity.__Accept(FnVisitor(), [&os, &osKey, &index, &fieldNames](auto &val) {
      if (index == 0) {
        osKey << fieldNames[index++] << "=";
        SerializeValue(osKey, val);
      } else {
        os << fieldNames[index++] << "=";
        if (index != fieldNames.size())
          SerializeValue(os, val) << ',';
        else
          SerializeValue(os, val);
      }
      return true;
    });
    // “set id=xx"
    // "where score=xx ..."
    _Update(entity, os.str(), osKey.str());
  }

  /**
   * @brief 范围更新
   *
   * @tparam In
   * @param entities
   */
  template <typename In>
  void UpdateRange(const In &entities) {
    using C = typename In::value_type;

    std::stringstream os;
    const auto &fieldNames = ORMapper::_FieldNames<C>();

    for (const auto &entity : entities) {
      os << "update " << C::__ClassName << " set ";

      size_t index = 0;
      std::stringstream osKey;

      entity.__Accept(FnVisitor(),
                      [&os, &osKey, &index, &fieldNames](auto &val) {
                        if (index == 0) {
                          osKey << fieldNames[index++] << "=";
                          SerializeValue(osKey, val);
                        } else {
                          os << fieldNames[index++] << "=";
                          if (index != fieldNames.size())
                            SerializeValue(os, val) << ',';
                          else
                            SerializeValue(os, val);
                        }
                        return true;
                      });
      os << " where " + osKey.str() + ";";
    }
    _connector.Execute(os.str());
  }

  /**
   * @brief 删除操作
   *
   * @tparam C
   * @param entity
   */
  template <typename C>
  void Delete(const C &entity) {
    std::stringstream os;
    os << "where ";

    entity.__Accept(FnVisitor(), [&os](auto &val) {
      os << ORMapper::_FieldNames<C>()[0] << "=";
      SerializeValue(os, val);
      return false;
    });

    _Delete(entity, os.str());
  }

  /**
   * @brief 查询类实现
   *
   * @tparam C
   */
  template <typename C>
  class ORQuery {
   public:
    ORQuery(const C &queryHelper, ORMapper *pMapper)
        : _queryHelper(queryHelper), _pMapper(pMapper) {}

    /**
     * @brief 查询操作
     *
     * @param expr 条件语句
     * @return ORQuery& 查询结果
     */
    inline ORQuery &Where(const Expr &expr) {
      _sqlWhere.clear();
      for (const auto exprStr : expr.expr) {
        // 获取类型名称
        if (exprStr.first != nullptr) _sqlWhere += _GetFieldName(exprStr.first);
        // ((name='July' and (id<=90 and id>=60)))
        _sqlWhere += exprStr.second;
      }
      return *this;
    }

    // Order by
    template <typename T>
    inline ORQuery &OrderBy(const T &property) {
      _sqlOrderBy = " order by " + _GetFieldName(&property);
      return *this;
    }

    template <typename T>
    inline ORQuery &Take(size_t count) {
      _sqlLimit = " limit " + std::to_string(count);
      return *this;
    }

    template <typename T>
    inline ORQuery &Skip(size_t count) {
      _sqlOffset = " offset " + std::to_string(count);
      return *this;
    }

    std::vector<C> ToVector() {
      static_assert(std::is_copy_constructible<C>::value,
                    "It must be Copy Constuctible");
      std::vector<C> ret;
      _pMapper->_Select(_queryHelper, ret, _GetCondSql());
      return std::move(ret);
    }

    std::list<C> ToList() {
      static_assert(std::is_copy_constructible<C>::value,
                    "It must be Copy Constructible");
      std::list<C> ret;
      _pMapper->_Select(_queryHelper, ret, _GetCondSql());
      return std::move(ret);
    }

    template <typename... Args>
    inline void Update(const Args &...fields) {
      std::stringstream setSql;
      setSql << "set ";
      _Set(setSql, fields...);
      _pMapper->_Update(_queryHelper, setSql.str(), _GetCondSql<true>());
    }

    inline void Delete() {
      _pMapper->_Delete(_queryHelper, _GetCondSql<true>());
    }

    inline size_t Count() {
      return (size_t)std::stoull(
          _pMapper->_Aggregate(_queryHelper, "count", "*", _GetCondSql()));
    }

    template <typename T>
    T Sum(const T &property) {
      T ret;
      DeserializeValue(
          ret, _pMapper->_Aggregate(_queryHelper, "sum",
                                    _GetFieldName(&property), _GetCondSql()));
      return ret;
    }

   private:
    const C &_queryHelper;

    ORMapper *_pMapper;

    std::string _sqlWhere;
    std::string _sqlOrderBy;
    std::string _sqlLimit, _sqlOffset;

    /**
     * @brief 获取元素名称
     *
     * @param property 类型地址
     * @return std::string
     */
    std::string _GetFieldName(const void *property) {
      size_t index = 0;
      _queryHelper.__Accept(FnVisitor(), [&property, &index](auto &val) {
        if (property == &val) return false;  // 地址匹配退出
        index++;
        return true;
      });
      // 获取元素名称
      const auto &fileNames = _pMapper->_FieldNames<C>();
      if (index == fileNames.size())
        throw std::runtime_error("No such Field in Table");
      return fileNames[index];
    }

    /**
     * @brief 拼接条件
     *
     * @tparam onlyWhere
     * @return std::string
     */
    template <bool onlyWhere = false>
    std::string _GetCondSql() const {
      if (onlyWhere) {
        if (!_sqlWhere.empty())
          return " where (" + _sqlWhere + ")";
        else
          return "";
      }
      if (!_sqlWhere.empty())
        return " where (" + _sqlWhere + ")" + _sqlOrderBy + _sqlLimit +
               _sqlOffset;
      else
        return _sqlOrderBy + _sqlLimit + _sqlOffset;
    }

    template <typename T, typename... Args>
    inline void _Set(std::ostream &os, const T &arg, const Args &...args) {
      _Set(os, arg);
      os << ",";
      _Set(os, args...);
    }

    template <typename T>
    inline void _Set(std::ostream &os, const T &arg) {
      os << _GetFieldName(&arg) << "=";
      SerializeValue(os, arg);
    }
  };

  template <typename C>
  ORQuery<C> Query(const C &queryHelper) {
    return ORQuery<C>(queryHelper, this);
  }

 private:
  SQLConnector _connector;

  /**
   * @brief 查询语句
   *
   * @tparam C
   * @tparam Out
   * @param copyable 输入类型
   * @param out 结果
   * @param sqlCond 条件
   */
  template <typename C, typename Out>
  void _Select(C copyable, Out &out, const std::string &sqlCond) {
    auto fieldCount = ORMapper::_FieldNames<C>().size();
    _connector.Execute(
        "select * from " + std::string(C::__ClassName) + " " + sqlCond + ";",
        [&](MYSQL_RES *result) {
          if (result) {
            size_t num_fields = mysql_num_fields(result);
            if (num_fields != fieldCount)
              throw std::runtime_error(
                  "Inter ORM Error: Select argc != Field Count");
            size_t num_rows = mysql_num_rows(result);
            for (size_t i = 0; i < num_rows; i++) {
              MYSQL_ROW row = mysql_fetch_row(result);
              size_t index = 0;
              copyable.__Accept(FnVisitor(), [&row, &index](auto &val) {
                DeserializeValue(val, row[index++]);
                return true;
              });
              out.push_back(copyable);
            }
          }
        });  // 回调函数返回查询结果 -> list
  }

  /**
   * @brief 计算sum、count等信息
   *
   * @tparam C
   * @param func sum/count
   * @param field 字段
   * @param sqlCond 条件
   * @return std::string 结果
   */
  template <typename C>
  std::string _Aggregate(const C &, const std::string &func,
                         const std::string field, const std::string &sqlCond) {
    std::string ret;
    _connector.Execute("select " + func + "(" + field + ") from " +
                           std::string(C::__ClassName) + " " + sqlCond + ";",
                       [&](MYSQL_RES *result) {
                         MYSQL_ROW row = mysql_fetch_row(result);
                         if (row) ret = row[0];
                       });  // 回掉函数返回结果
    if (ret.empty())
      throw std::runtime_error(
          "SQL error: Return null at 'select " + func + " (" + field +
          ") as t from " + std::string(C::__ClassName) + " " + sqlCond + ";'");
    return ret;
  }

  /**
   * @brief 更新的具体sql命令拼接
   *
   * @tparam C
   * @param sqlSet set命令
   * @param sqlCond where命令
   */
  template <typename C>
  void _Update(const C &, const std::string &sqlSet,
               const std::string &sqlCond) {
    _connector.Execute("update " + std::string(C::__ClassName) + " " + sqlSet +
                       " " + sqlCond + ";");
  }

  /**
   * @brief 删除的具体sql命令拼接
   *
   * @tparam C
   * @param sqlCond 删除条件
   */
  template <typename C>
  void _Delete(const C &, const std::string &sqlCond) {
    _connector.Execute("delete from " + std::string(C::__ClassName) + " " +
                       sqlCond + ";");
  }

  /**
   * @brief 返回各个参数名称
   *
   * @tparam C 待分析的类型
   * @return const std::vector<std::string>&
   */
  template <typename C>
  static const std::vector<std::string> &_FieldNames() {
    auto _ExtractFieldName = []() {
      // *__FieldNames = "id, name, score"
      std::string rawStr(C::__FieldNames), tmpStr;
      rawStr.push_back(',');
      tmpStr.reserve(rawStr.size());

      std::vector<std::string> ret;
      for (const auto &ch : rawStr) {
        switch (ch) {
          case ',':
            ret.push_back(tmpStr);
            tmpStr.clear();
            break;

          default:
            if (isalnum(ch) || ch == '_') tmpStr += ch;
            break;
        }
      }
      return std::move(ret);
    };

    static const std::vector<std::string> _fieldNames(_ExtractFieldName());
    // [[id],[name],[score]]
    return _fieldNames;
  }
};

template <typename T>
Expr_Field<T> Field(T &property) {
  return Expr_Field<T>{property};
}

#endif  // INCLUDE_ORM_MAPPER_H_
